package com.lechi.web.runner;

import com.lechi.web.common.config.LeChiConfig;
import com.lechi.web.common.utils.DateUtils;
import com.lechi.web.common.utils.StringUtils;
import com.lechi.web.constants.Constants;
import com.lechi.web.dto.IotDeviceCmdParseDto;
import com.lechi.web.framework.scheduler.DynamicJob;
import com.lechi.web.framework.scheduler.DynamicSchedulerFactory;
import com.lechi.web.framework.util.GuavaCacheUtil;
import com.lechi.web.job.GetRecordFileNameJob;
import com.lechi.web.service.IIotDeviceCmdService;
import com.lechi.web.service.IIotWeatherDictService;
import com.lechi.web.system.domain.*;
import com.lechi.web.system.mapper.iot.IotDeviceMapper;
import com.lechi.web.util.FunctionUtil;
import io.netty.channel.epoll.Epoll;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.quartz.SchedulerException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.IntStream;

/**
 * 项目启动后缓存传感器指令解析格式
 */
@Slf4j
@Component("CacheCmdParseMsg")
@Order(4)
public class CacheCmdParseMsg implements CommandLineRunner {

    @Autowired
    private IIotDeviceCmdService iotDeviceCmdService;

    @Autowired
    private IIotWeatherDictService iotWeatherDictService;

    @Autowired
    private DynamicSchedulerFactory dynamicSchedulerFactory;

    @Autowired
    private IotDeviceMapper iotDeviceMapper;

    @Autowired
    private LeChiConfig leChiConfig;

    public void run(String... args) {
        if ("DaPeng".equals(leChiConfig.getName())) {
            return;
        }

        String gateWayType = args != null && args.length > 0 ? args[0] : null;
        if (Constants.WEATHER2017.equals(gateWayType)) {
            cacheWeather2017();
        } else if (Constants.WEATHER2024.equals(gateWayType)) {
            cacheWeather2024();
        } else {
            cacheWeather2017();
            cacheWeather2024();
            cacheModbusTcp();
            initQualityCodeMap();
        }
    }

    /**
     * 缓存 Modbus TCP 设备的指令解析格式
     *
     */
    private void cacheModbusTcp() {
        List<IotDeviceCmdSchedule> iotDeviceList = iotDeviceCmdService.selectIotDeviceCmdListForSchedule(null);
        List<IotDeviceCmdParse> iotDeviceCmdCreateList = iotDeviceCmdService.selectIotDeviceCmdListByOrder(null);

        iotDeviceList.forEach(iotDeviceCmdSchedule -> {
            List<IotDeviceCmdParseDto> retList = new ArrayList<>();
            String deviceSNAndAddr = iotDeviceCmdSchedule.getDeviceSn() + "_" + iotDeviceCmdSchedule.getStationAddr();
            iotDeviceCmdCreateList.stream().
                    filter(iotDeviceCmdCreate -> Objects.equals(deviceSNAndAddr, iotDeviceCmdCreate.getDeviceSn() + "_" + iotDeviceCmdCreate.getStationAddr()))
                    .forEach(iotDeviceCmdCreate -> {
                        IotDeviceCmdParseDto iotDeviceCmdParseDto = new IotDeviceCmdParseDto();
                        BeanUtils.copyProperties(iotDeviceCmdCreate, iotDeviceCmdParseDto);
                        if (Constants.FLOAT_FORMAT_32.equals(iotDeviceCmdCreate.getDataFormat()) || Constants.INTEGER_DATA_FORMAT_32.equals(iotDeviceCmdCreate.getDataFormat())) {
                            iotDeviceCmdParseDto.setBeginIndex((iotDeviceCmdCreate.getOffsetCode()  - 1) * 2);
                            iotDeviceCmdParseDto.setEndIndex(iotDeviceCmdParseDto.getBeginIndex() + 4);
                        } else if (Constants.FLOAT_FORMAT_16.equals(iotDeviceCmdCreate.getDataFormat()) || Constants.INTEGER_DATA_FORMAT_16.equals(iotDeviceCmdCreate.getDataFormat())) {
                            iotDeviceCmdParseDto.setBeginIndex((iotDeviceCmdCreate.getOffsetCode() - 1) * 2);
                            iotDeviceCmdParseDto.setEndIndex(iotDeviceCmdParseDto.getBeginIndex() + 2);
                        }
                        retList.add(iotDeviceCmdParseDto);
                    });
            GuavaCacheUtil.getCmdParseTableMap().put(deviceSNAndAddr, retList);
        });
        IotDeviceBo iotDeviceBo = new IotDeviceBo();
        iotDeviceBo.setProtocolType(String.format("'%s'", String.join("','", Constants.MODBUS_RTU)));
        List<IotDeviceBo> iotDeviceBoList = iotDeviceMapper.selectIotDeviceList(iotDeviceBo);
        iotDeviceBoList.stream().map(FunctionUtil.modbusRtu).forEach(this::initWriteRecordTask);

    }

    /**
     * 缓存 TCP 天气数据的解析格式
     */
    public void cacheWeather2017() {
        IotWeatherDict iotWeatherDict = getIotWeatherDict(Constants.WEATHER2017);
        final List<IotWeatherDeviceBo> weatherDeviceList = iotWeatherDictService.selectIotWeatherDeviceList(iotWeatherDict).stream()
                .map(FunctionUtil.weather2017).toList();
        GuavaCacheUtil.getCmdParseTableMap().put(Constants.WEATHER2017, weatherDeviceList);
        final List<IotWeatherSensorParseBo> weatherSensorsList = iotWeatherDictService.selectIotWeatherSensorList(iotWeatherDict);
        for (IotWeatherDeviceBo deviceDto : weatherDeviceList) {
            String deviceFlag = deviceDto.getDeviceStation();
            Map<String, IotWeatherSensorParseBo> sensorParseBoMap = new ConcurrentHashMap<>();
            for (IotWeatherSensorParseBo sensorBo : weatherSensorsList) {
                String sensorFlag = sensorBo.getDeviceStation();
                if (Objects.equals(sensorFlag, deviceFlag)) {
                    sensorParseBoMap.put(sensorBo.getSensorSign().trim(), sensorBo);
                }
            }
            GuavaCacheUtil.getWeatherParseTableMap().put(deviceFlag, sensorParseBoMap);
            GuavaCacheUtil.getDeviceIdAndInstrumentIdMap().put(deviceDto.getDeviceId(), deviceDto.getInstrumentId());
            initWriteRecordTask(deviceDto);
        }
    }

    @NotNull
    private static IotWeatherDict getIotWeatherDict(String protocolType) {
        IotWeatherDict iotWeatherDict = new IotWeatherDict();
        iotWeatherDict.setProtocolType(protocolType);
        return iotWeatherDict;
    }

    /**
     * 缓存 TCP 天气数据的解析格式
     *
     */
    public void cacheWeather2024() {
        IotWeatherDict iotWeatherDict = getIotWeatherDict(Constants.WEATHER2024);
        final List<IotWeatherDeviceBo> weatherDeviceList = iotWeatherDictService.selectIotWeatherDeviceList(iotWeatherDict).stream()
                .map(FunctionUtil.weather2024).toList();
        GuavaCacheUtil.getCmdParseTableMap().put(Constants.WEATHER2024, weatherDeviceList);
        var weatherSensorsList = iotWeatherDictService.selectIotWeatherSensorList(iotWeatherDict);
        for (IotWeatherDeviceBo deviceDto : weatherDeviceList) {
            String deviceFlag = deviceDto.getDeviceStation();
            Map<String, IotWeatherSensorParseBo> sensorParseBoMap = new ConcurrentHashMap<>();
            for (IotWeatherSensorParseBo sensorBo : weatherSensorsList) {
                if (Objects.equals(sensorBo.getDeviceStation(), deviceFlag)) {
                    sensorParseBoMap.put(sensorBo.getSensorSign().trim(), sensorBo);
                }
            }
            GuavaCacheUtil.getDeviceIdAndInstrumentIdMap().put(deviceDto.getDeviceId(), deviceDto.getInstrumentId());
            GuavaCacheUtil.getWeatherParseTableMap().put(deviceFlag, sensorParseBoMap);
            initWriteRecordTask(deviceDto);
        }
    }

    /**
     * 初始化写日志文件任务
     */
    public void initWriteRecordTask(IotWeatherDeviceBo deviceDto) {
        if (Objects.isNull(deviceDto.getIsRecordLog()) || Objects.isNull(deviceDto.getRecordLogTime()) || deviceDto.getIsRecordLog() != 1) {
            return;
        }
        String fileName = StringUtils.join(deviceDto.getDeviceStation(), "_", deviceDto.getDeviceSign(), "_watchArea_value_");
        if (Constants.MODBUS_RTU.equals(deviceDto.getProtocolType())) {
            fileName = StringUtils.join(deviceDto.getDeviceStation(), "_value_");
        } else if (Constants.WEATHER2024.equals(deviceDto.getProtocolType())) {
            fileName = StringUtils.join(deviceDto.getDeviceStation(), "_", deviceDto.getDeviceSign(), "_value_");
        }
        var jobName = Constants.WRITE_RECORD_LOG_TXT + deviceDto.getDeviceId();
        try {
            DynamicJob writeRecordLogJob = new DynamicJob(jobName)
                    .cronExpression(String.valueOf(deviceDto.getRecordLogTime() * 60))
                    .target(GetRecordFileNameJob.class);
            writeRecordLogJob.addJobData("deviceId", deviceDto.getDeviceId());
            writeRecordLogJob.addJobData("fileName", fileName);
            dynamicSchedulerFactory.removeJob(writeRecordLogJob);
            Date date = Epoll.isAvailable() ? DateUtils.weatherMsgWriteBeginDate() : new Date() ;
            dynamicSchedulerFactory.registerJob(writeRecordLogJob, date);
            log.error("注册写入观测数据文件任务 [{}] -> ok", writeRecordLogJob);
        } catch (SchedulerException e) {
            log.error("注册写入观测数据文件任务 异常", e);
        }
    }

    private void initQualityCodeMap() {
        IntStream.range(0, 99).forEach(i -> GuavaCacheUtil.getStateCodeMap().put(String.valueOf(i), true));
        GuavaCacheUtil.getStateCodeMap().putAll(Map.of("00", true, "01", true, "02", true, "03", true, "04", true, "05", true,"06", true, "07", true, "08", true, "09", true));
    };
}


